package com.mayflygeek.mayflyask.framework.webmvc.controller;

import java.sql.Date;
import java.text.SimpleDateFormat;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.core.convert.ConversionService;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.validation.Validator;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;

import com.mayflygeek.mayflyask.framework.webmvc.Application;
import com.mayflygeek.mayflyask.framework.webmvc.annotation.After;
import com.mayflygeek.mayflyask.framework.webmvc.annotation.Before;
import com.mayflygeek.mayflyask.framework.webmvc.event.AbstractEventSupport;
import com.mayflygeek.mayflyask.framework.webmvc.event.Event;
import com.mayflygeek.mayflyask.framework.webmvc.event.EventListener;
import com.mayflygeek.mayflyask.framework.webmvc.message.Flash;
import com.mayflygeek.mayflyask.framework.webmvc.message.Message;
import com.mayflygeek.mayflyask.framework.webmvc.message.MessageBuilder;
import com.mayflygeek.mayflyask.framework.webmvc.util.RequestUtil;
import com.mayflygeek.mayflyask.framework.webmvc.util.SearchUtil;

/**
 * @author 虫虫
 * @team 蜉蝣极客
 * @link www.mayflygeek.com
 * @Emial chongchong@mayflygeek.com
 * 
 */
public class BaseController extends AbstractEventSupport implements EventListener {
	public static final String MODEL_RESULT_KEY = "result";
	public static final String QUERYSTRING_NAME = "queryString";
	public static final String RESOURCE_URI_NAME = "resuri";
	private static Logger logger = LoggerFactory.getLogger(BaseController.class);

	private String[] _controllerMappings = null;

	@Resource
	private ConversionService conversionService;

	@Resource
	private Validator validator;

	public BaseController() {
		// 将自身添加到事件监听队列
		addListener(this);
	}

	@InitBinder
	protected void initBinder(WebDataBinder binder) {
		registerCustomEidtorsForWebDataBinder(binder);
	}

	@Before
	public void beforeHandler(HttpServletRequest request, HttpServletResponse response) {
		// do nothing
	}

	/**
	 * 每次Action执行完后调用
	 * 
	 * @param request
	 * @param response
	 */
	@After
	public void afterHandler(HttpServletRequest request, HttpServletResponse response) {
		// 设置当前 Resource uri
		request.setAttribute(RESOURCE_URI_NAME, getResourceURI());
	}

	/**
	 * 事件处理。子类覆盖它，在当前Controller内接收并处理事件
	 */
	@Override
	public void handleEvent(Event event) {
		logger.info("事件处理");
		// TODO Auto-generated method stub

	}

	/**
	 * 获取Flash对象
	 */
	protected Flash flash() {
		return Application.getFlash();
	}

	/**
	 * 获取Flash对象，使用model保存值
	 */
	protected Flash flash(Map<String, Object> model) {
		return Application.getFlash(model);
	}

	/**
	 * 如果model里不存在key，则将value添加到model
	 * 
	 * @param model
	 * @param key
	 * @param value
	 */
	protected final void safeAddValueToModel(ModelMap model, String key, Object value) {
		if (model.containsKey(key)) {
			model.put(key, value);
		}
	}

	/**
	 * 自动绑定请求参数值到对应的JavaBean
	 * <p/>
	 * 需参数名和属性名匹配
	 * 
	 * @see #populateBeanFromRequest(object, boolean)
	 * @param bean
	 * @return
	 */
	protected final BindingResult bind(Object bean) {
		final boolean custom = false;// 默认使用jsr303:Bean Validation
		return bind(bean, custom);
	}

	protected final BindingResult bind(Object bean, boolean custom) {
		ServletRequestDataBinder binder = new ServletRequestDataBinder(bean);

		binder.setValidator(validator);
		binder.setConversionService(conversionService);

		if (custom) {
			registerCustomEidtorsForWebDataBinder(binder);
		}

		binder.bind(Application.getRequest());

		binder.validate();

		return binder.getBindingResult();
	}

	/**
	 * 数据绑定错误判定
	 * 
	 * @param bindingResult
	 * @param model
	 * @return boolean 绑定有错则true，否则false
	 */
	protected boolean withErrors(BindingResult bindingResult, ModelMap model) {
		if (bindingResult != null && bindingResult.hasErrors()) {
			flash(model).setMessage(MessageBuilder.error("输入有误！").data(bindingResult).build());
			logger.error("withErrors.输入有误！");
			return true;
		}
		return false;
	}

	/**
	 * 绑定数据错误信息
	 * 
	 * @param result
	 * @return
	 */
	protected String getBindingErrorsString(BindingResult result) {
		StringBuilder sb = new StringBuilder();
		List<ObjectError> errors = result.getAllErrors();
		for (ObjectError error : errors) {
			sb.append(error.getDefaultMessage()).append("\n");
		}
		return sb.toString();
	}

	/**
	 * 注册自定义类型转换器
	 * 
	 * @param binder
	 */
	protected void registerCustomEidtorsForWebDataBinder(WebDataBinder binder) {
		// 日期格式化
		SimpleDateFormat dateFormat = new SimpleDateFormat(getBinderDatePattern());
		dateFormat.setLenient(false);
		binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
	}

	/**
	 * 返回日期格式化
	 * 
	 * @return 默认yyyy-MM-dd
	 */
	protected String getBinderDatePattern() {
		return "yyyy-MM-dd";
	}

	/**
	 * 是否Ajax请求 Header["x-requested-with"] != null
	 * 
	 * @return boolean 是返回true 否返回false
	 */
	protected boolean isAjax() {
		return RequestUtil.isAjax(Application.getRequest());
	}

	/**
	 * 视图名称。
	 * <p/>
	 * 规则： Controller 的RequestMapping的第一个映射/subview
	 * 
	 * <pre>
	 * <code>
	 * &#64;RequestMapping("modules/protects")
	 * class ProjectController extends BaseController{}
	 * </code>
	 * <p/>
	 * 视图模板的位置 /modules/projects index.jsp show.jsp ...
	 * 
	 * <pre>
	 */
	protected String view(String subview) {
		String viewdir = getControllerFirstMapping();
		return viewdir + "/" + subview;
	}

	/**
	 * 
	 * @param path
	 * @return
	 */
	protected String redirect(String path) {
		return "redirect:" + (!path.startsWith("/") ? "/" + path : path);
	}

	/**
	 * 记录异常信息并保存失败Result
	 * 
	 * @param exception
	 *            异常
	 * @param model
	 *            ModelMap
	 * @param message
	 *            提示信息
	 */
	protected void logExceptionMessage(Exception exception, ModelMap model, String message) {
		flash(model).setMessage(logExceptionMessage(exception, message));
	}

	protected Message logExceptionMessage(Exception exception, String message) {
		logger.error(message, exception);
		return MessageBuilder.error(message + ": " + exception.getMessage()).build();
	}

	/**
	 * 将request 请求参数存放到ModelMap对象
	 * 
	 * @param model
	 */
	protected void storeRequestParams(ModelMap model) {
		final HttpServletRequest request = Application.getRequest();
		Enumeration<String> names = request.getParameterNames();
		while (names.hasMoreElements()) {
			String name = names.nextElement();
			String[] values = request.getParameterValues(name);
			model.put(name, values.length > 1 ? values : values[0]);
		}
	}

	/**
	 * redirect 主页
	 */
	protected String redirectIndex() {
		;
		return redirect(getControllerFirstMapping());
	}

	/**
	 * redirect 显示待定项
	 * 
	 * @param id
	 * @return
	 */
	protected String redirectShow(Long id) {
		return redirect(getControllerFirstMapping() + "/" + id);
	}

	/**
	 * 将查询字符串存放到ModelMap对象
	 * 
	 * @param model
	 */
	protected void storeQueryString(ModelMap model) {
		model.put(QUERYSTRING_NAME, SearchUtil.searchParam2QueryString(Application.getRequest()));
	}

	/**
	 * 获取控制器RequestMapping value.
	 * <p>
	 * 
	 * <pre>
	 * <code>
	 * &#64;RequestMapping("/modules/users")
	 * public class UserController extends BaseController{}
	 * </pre>
	 * 
	 * </code> UserController.getControllerMappings 返回 "/modules/users"
	 * </p>
	 * 
	 */
	protected final String[] getControllerMappings() {
		if (_controllerMappings == null) {
			final RequestMapping rm = this.getClass().getAnnotation(RequestMapping.class);
			_controllerMappings = (rm != null) ? rm.value() : new String[] {};
		}
		return _controllerMappings;
	}

	/**
	 * 获取控制类第一个RequestMapping
	 */
	protected final String getControllerFirstMapping() {
		final String[] controllerMappings = getControllerMappings();
		return controllerMappings.length == 0 ? null : controllerMappings[0];
	}

	public void setValidator(Validator validator) {
		this.validator = validator;
	}

	public void setConversionService(ConversionService conversionService) {
		this.conversionService = conversionService;
	}

	private String getResourceURI() {
		String reqMapping = this.getControllerFirstMapping();
		if (reqMapping == null) {
			reqMapping = "/";
		}

		final String contextPath = Application.getContextPath();
		final String spe = (contextPath.endsWith("/") || reqMapping.startsWith("/")) ? "" : "/";
		return contextPath + spe + reqMapping;
	}

}
